#ifndef __DISKMD_H__
#define __DISKMD_H__

#include <stdint.h>
#include <semaphore.h>
#include <errno.h>
#include <libaio.h>

#include "ynet_rpc.h"
#include "sysy_conf.h"
#include "chunk.h"
#include "buffer.h"

#define DISK_MAX 256
#define DISK_TIER_MAX 2
#define POOL_MAX 256
#define POOL_DEFAULT "default"

/*
 * 磁盘分为三种状态
 * 1. writable 正常状态
 * 2. offline 磁盘发生EIO
 * 3. detecting 磁盘处于删除状态 不在提供服务,并且磁盘数据需要被恢复
 * 目前状态混乱　都是以offline区分的　offline后触发数据恢复
 */

// #define __DISK_WRITEABLE__ __NODE_STAT_WRITEABLE__
#define __DISK_DELETING__ __NODE_STAT_DELETING__
#define __DISK_OFFLINE__  __NODE_STAT_OFFLINE__

// #define NODE_DISK_OFFLINE     1
// #define NODE_DISK_ONLINE      0

typedef struct {
        uint32_t diskid;
        uint32_t idx;
} diskloc_t;

typedef enum {
        DISKIO_CACHE = 1,
        DISKIO_SYNC = 2,
        DISKIO_DIRECT = 3,
} diskio_type_t;

typedef enum {
        __DISK_ROLE_SSD__,
        __DISK_ROLE_HDD__,
        __DISK_ROLE_NONE__,
        __DISK_ROLE_CACHED__,
} disk_role_t;  /*by zsy to change it to disk role, it is disk role rather than disk type. disk type should be ram, disk, nvme etc.*/

typedef struct {
        uint64_t used;
        uint64_t total;
        char name[MAX_NAME_LEN];
} poolstat_t;

typedef struct {
        int pool_count;
        poolstat_t pool_stat[POOL_MAX];
} nodedfree_t;


typedef int (*writeback_callback_t)(int diskid, uint64_t *chunk_count);

// diskmd
int  diskmd_init(const char *home, uint64_t *max_chunk);
void diskmd_close();

// diskmd_async.c
int diskmd_async_init();

//node
int diskmd_writeable();
int diskmd_node_dfree(nodedfree_t *dfree);
int diskmd_update_latency(int diskid, uint64_t latency);

// pool
int diskmd_pool_dfree(const char *pool_name, uint64_t *_disk_total, uint64_t *_disk_used);
int diskmd_pool_cleanup(const char *pool);
int diskmd_pool_getcapacity(const char *pool, int tier, uint64_t *_total, uint64_t *_used);
int diskmd_pool_writeable(const char *pool, int force);

// disk
int diskmd_online(int diskid, int *online);
int diskmd_set_online(int diskid, int *changed);

int  diskmd_set_deleting(uint32_t idx, int deleting);
void diskmd_remove(int idx);
int  diskmd_destroy(int idx);

int diskmd_gettier(int diskid, int *tier);
int diskmd_get_cache(int diskid, int *cache, int *cached);
int diskmd_get_pool(int diskid, char *pool_name);

// diskloc
int diskmd_create(const char *pool, diskloc_t *locs, int loc_count, int *_tier, int priority);
int diskmd_create_init(const chkid_t *chkid, const diskloc_t *loc, int zero, const buffer_t *initdata);
int diskmd_create_direct(const char *pool_name, diskloc_t *locs, int locs_count, int *_tier);
int diskmd_create_with_tier(const char *pool_name, diskloc_t *locs, int loc_count, int tier);
int diskmd_unlink(const chkid_t *chkid, const diskloc_t *loc, uint64_t meta_version);

void diskmd_exists(const diskloc_t *loc, int exist);
int  diskmd_delete(const diskloc_t *loc);
int  diskmd_chunk_load(const chkid_t *chkid, diskloc_t *loc, chkid_t *parent, char *pool);

void diskmd_chunk_iterator_cursor(const char *table, int cursor, const char *condition, func_va_t func, void *arg);

//io
int diskmd_aio_init();
int diskmd_aio_read(const chkid_t *chkid, const diskloc_t *loc, buffer_t *buf, int _offset, int prio);
int diskmd_aio_write(const chkid_t *chkid, const diskloc_t *loc, const buffer_t *buf, int _offset, int prio);
int diskmd_aio_fsync(int fd, int prio);

int diskmd_flush();
int diskmd_flush_range(const diskloc_t *loc);
int diskmd_lost_recovery(const char* name);
int diskmd_disk_recovery_flag(const char *name);

int diskmd_real_path(char *path, struct stat* stbuf); //do it later.

#define DISKMD_ANALYSIS_BEGIN(mark)                    \
        struct timeval t1##mark, t2##mark;      \
        int used##mark;                         \
                                                \
        _gettimeofday(&t1##mark, NULL);         \


#define DISKMD_ANALYSIS_UPDATE(mark, __loc__, __usec, __str)             \
        _gettimeofday(&t2##mark, NULL);                                 \
        used##mark = _time_used(&t1##mark, &t2##mark);                  \
        diskmd_update_latency((__loc__)->diskid, used##mark);           \
        if (used##mark > (__usec)) {                                    \
                if (used##mark > 1000 * 1000 * gloconf.rpc_timeout) {   \
                        DWARN_PERF("analysis used %f s %s, timeout\n", (double)(used##mark) / 1000 / 1000, (__str) ? (__str) : ""); \
                } else {                                                \
                        DINFO_PERF("analysis used %f s %s\n", (double)(used##mark) / 1000 / 1000, (__str) ? (__str) : ""); \
                }                                                       \
        }                                                               \

#endif
